package interceptor

import (
	"bytes"
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"io/ioutil"
	"net/http"
	"time"
)

// CustomResponseWriter 封装 gin ResponseWriter 用于获取回包内容。
type CustomResponseWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (w CustomResponseWriter) Write(b []byte) (int, error) {
	w.body.Write(b)
	return w.ResponseWriter.Write(b)
}

// LoggerMiddleware 打印每个接口的请求与响应参数，通过ginServer.Use(interceptor.LoggerMiddleware())调用。
func LoggerMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 记录请求的时间
		startTime := time.Now()

		// 使用自定义 ResponseWriter
		crw := &CustomResponseWriter{
			body:           bytes.NewBufferString(""),
			ResponseWriter: c.Writer,
		}
		// 替换 context 中的 writer
		c.Writer = crw

		// 打印请求信息
		reqBody, _ := c.GetRawData()
		// 重新设置请求体供后续中间件和处理程序使用
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody)) // 重新设置请求体

		var prettyJSON bytes.Buffer
		if err := json.Compact(&prettyJSON, reqBody); err != nil {
			logrus.Errorf("请求体格式化失败: %v", err)
		}

		logRequest := logrus.WithFields(logrus.Fields{
			"Method":      c.Request.Method,
			"URL":         c.Request.RequestURI,
			"RequestBody": prettyJSON.String(),
		})
		logRequest.Info()

		// 执行请求处理程序和其他中间件函数
		c.Next()

		// 获取HTTP响应的状态码
		statusCode := c.Writer.Status()
		if len(c.Errors) > 0 {
			// len(c.Errors) > 0 表示请求过程中是否产生了错误，c.Errors.ByType(gin.ErrorTypePrivate) 用于筛选出私有类型的错误。
			logrus.Error(c.Errors.ByType(gin.ErrorTypePrivate))
		}
		if statusCode >= 500 {
			// 表示服务器内部错误 (5xx)，记录为错误日志。
			logrus.Errorf("服务端错误: %s", http.StatusText(statusCode))
		} else if statusCode >= 400 {
			// 表示客户端错误 (4xx)，记录为警告日志。
			logrus.Warnf("客户端错误: %s", http.StatusText(statusCode))
		} else {
			// 其他情况（即状态码小于400），记录为信息日志。
			endTime := time.Now()
			spendTime := endTime.Sub(startTime)
			responseBody := string(crw.body.Bytes())
			logResponse := logrus.WithFields(logrus.Fields{
				"URL":          c.Request.RequestURI,
				"SpendTime":    spendTime,
				"ResponseBody": responseBody,
			})
			logResponse.Info()
		}
	}
}
